1mod archive;
2#[allow(dead_code)]
3mod consts;
4mod crypt;
5mod pe;
6mod read;
7mod reader;
8mod segmenter;
9mod writer;
10
11use crate::ext::io::*;
12use crate::scripts::base::*;
13use crate::types::*;
14use anyhow::Result;
15use clap::ValueEnum;
16use consts::ZSTD_SIGNATURE;
17use crypt::Crypt;
18pub use crypt::get_supported_games;
19pub use crypt::get_supported_games_with_title;
20use flate2::read::ZlibDecoder;
21use overf::wrapping;
22pub use segmenter::SegmenterConfig;
23use std::io::{Read, Seek, SeekFrom, Write};
24use std::sync::{Arc, Mutex};
25use writer::Xp3ArchiveWriter;
26use zstd::stream::read::Decoder as ZstdDecoder;
27
28pub fn parse_segmenter_config(str: &str) -> Result<SegmenterConfig> {
29 let parts: Vec<&str> = str.split(':').collect();
30 if parts.is_empty() {
31 return Ok(SegmenterConfig::default());
32 }
33 match parts[0].to_lowercase().as_str() {
34 "none" => Ok(SegmenterConfig::None),
35 "cdc" => {
36 if parts.len() != 4 {
37 return Err(anyhow::anyhow!(
38 "Invalid FastCDC segmenter config. Expected format: fastcdc,min_size,avg_size,max_size"
39 ));
40 }
41 let min_size = parse_size::parse_size(parts[1])?;
42 let avg_size = parse_size::parse_size(parts[2])?;
43 let max_size = parse_size::parse_size(parts[3])?;
44 if min_size == 0 || avg_size == 0 || max_size == 0 {
45 return Err(anyhow::anyhow!(
46 "Invalid FastCDC segmenter config. Sizes must be greater than 0."
47 ));
48 }
49 if !(min_size <= avg_size && avg_size <= max_size) {
50 return Err(anyhow::anyhow!(
51 "Invalid FastCDC segmenter config. Expected min_size <= avg_size <= max_size."
52 ));
53 }
54 Ok(SegmenterConfig::FastCdc {
55 min_size: min_size as u32,
56 avg_size: avg_size as u32,
57 max_size: max_size as u32,
58 })
59 }
60 "fixed" => {
61 if parts.len() != 2 {
62 return Err(anyhow::anyhow!(
63 "Invalid Fixed segmenter config. Expected format: fixed,size"
64 ));
65 }
66 let size = parse_size::parse_size(parts[1])?;
67 if size == 0 {
68 return Err(anyhow::anyhow!(
69 "Invalid Fixed segmenter config. Size must be greater than 0."
70 ));
71 }
72 Ok(SegmenterConfig::Fixed(size as usize))
73 }
74 "custom" => {
75 if parts.len() != 2 {
76 return Err(anyhow::anyhow!(
77 "Invalid Fixed segmenter config. Expected format: custom:json_path"
78 ));
79 }
80 let json_path = parts[1];
81 let data = std::fs::read_to_string(json_path)?;
82 Ok(SegmenterConfig::Custom(serde_json::from_str(&data)?))
83 }
84 _ => Err(anyhow::anyhow!("Unknown segmenter type: {}", parts[0])),
85 }
86}
87
88#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
89pub enum FileHashOption {
91 Both,
93 WithName,
95 WithoutName,
97}
98
99impl Default for FileHashOption {
100 fn default() -> Self {
101 Self::Both
102 }
103}
104
105#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
106pub enum PathHashOption {
107 Both,
109 NameOnly,
111}
112
113impl Default for PathHashOption {
114 fn default() -> Self {
115 Self::Both
116 }
117}
118
119#[derive(Debug)]
120pub struct Xp3ArchiveBuilder {}
122
123impl Xp3ArchiveBuilder {
124 pub fn new() -> Self {
126 Self {}
127 }
128}
129
130impl ScriptBuilder for Xp3ArchiveBuilder {
131 fn default_encoding(&self) -> Encoding {
132 Encoding::Utf8
133 }
134
135 fn default_archive_encoding(&self) -> Option<Encoding> {
136 Some(Encoding::Utf8)
137 }
138
139 fn build_script(
140 &self,
141 buf: Vec<u8>,
142 filename: &str,
143 _encoding: Encoding,
144 _archive_encoding: Encoding,
145 config: &ExtraConfig,
146 _archive: Option<&Box<dyn Script>>,
147 ) -> Result<Box<dyn Script + Send + Sync>> {
148 let mut base_offset = 0;
149 if buf.starts_with(b"MZ") {
150 base_offset = pe::get_base_offset(&buf)?;
151 }
152 Ok(Box::new(Xp3Archive::new(
153 MemReader::new(buf),
154 config,
155 filename,
156 base_offset,
157 )?))
158 }
159
160 fn build_script_from_file(
161 &self,
162 filename: &str,
163 _encoding: Encoding,
164 _archive_encoding: Encoding,
165 config: &ExtraConfig,
166 _archive: Option<&Box<dyn Script>>,
167 ) -> Result<Box<dyn Script + Send + Sync>> {
168 let mut file = std::fs::File::open(filename)?;
169 let mut base_offset = 0;
170 if file.peek_and_equal(b"MZ").is_ok() {
171 let mp = pelite::FileMap::open(filename)?;
172 base_offset = pe::get_base_offset(&mp)?;
173 }
174 Ok(Box::new(Xp3Archive::new(
175 file,
176 config,
177 filename,
178 base_offset,
179 )?))
180 }
181
182 fn build_script_from_reader<'a>(
183 &self,
184 mut reader: Box<dyn ReadSeek + Send + Sync + 'a>,
185 filename: &str,
186 _encoding: Encoding,
187 _archive_encoding: Encoding,
188 config: &ExtraConfig,
189 _archive: Option<&Box<dyn Script>>,
190 ) -> Result<Box<dyn Script + Send + Sync + 'a>> {
191 let mut base_offset = 0;
192 if reader.peek_and_equal(b"MZ").is_ok() {
193 let mut data = Vec::new();
194 let pos = reader.stream_position()?;
195 reader.read_to_end(&mut data)?;
196 reader.seek(SeekFrom::Start(pos))?;
197 base_offset = pe::get_base_offset(&data)?;
198 }
199 Ok(Box::new(Xp3Archive::new(
200 reader,
201 config,
202 filename,
203 base_offset,
204 )?))
205 }
206
207 fn extensions(&self) -> &'static [&'static str] {
208 &["xp3", "bin", "dat", "exe"]
209 }
210
211 fn script_type(&self) -> &'static ScriptType {
212 &ScriptType::KirikiriXp3
213 }
214
215 fn is_archive(&self) -> bool {
216 true
217 }
218
219 fn create_archive(
220 &self,
221 filename: &str,
222 files: &[&str],
223 _encoding: Encoding,
224 config: &ExtraConfig,
225 ) -> Result<Box<dyn Archive>> {
226 Ok(Box::new(Xp3ArchiveWriter::new(filename, files, config)?))
227 }
228
229 fn is_this_format(&self, filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
230 if buf_len >= 11 && buf.starts_with(consts::XP3_MAGIC) {
231 return Some(100);
232 }
233 if buf_len >= 2 && buf.starts_with(b"MZ") {
234 let p = std::path::Path::new(filename);
235 if p.exists() {
236 if let Ok(file) = pelite::FileMap::open(p) {
237 if pe::get_base_offset(&file).is_ok() {
238 return Some(100);
239 }
240 }
241 }
242 }
243 None
244 }
245}
246
247#[derive(Debug)]
248pub struct Xp3Archive<'a> {
250 archive: archive::Xp3Archive<'a>,
251 decrypt_simple_crypt: bool,
252 decompress_mdf: bool,
253 force_extract: bool,
254 force_decrypt: bool,
255}
256
257impl<'a> Xp3Archive<'a> {
258 pub fn new<T: Read + Seek + std::fmt::Debug + Send + Sync + 'a>(
259 stream: T,
260 config: &ExtraConfig,
261 filename: &str,
262 base_offset: u64,
263 ) -> Result<Self> {
264 let mut archive = archive::Xp3Archive::new(stream, config, filename, base_offset)?;
265 if config.xp3_debug_archive {
266 println!("Debug info for {}:\n{:#?}", filename, archive);
267 let _ = std::io::stdout().flush();
269 }
270 archive.entries.retain(|entry| {
271 let i = &entry.name;
272 !(i.find("$$$ This is a protected archive. $$$").is_some()
273 || i.find("$$$ This is a protectet archive. $$$").is_some()
275 || (i.to_lowercase().ends_with(".nene") && entry.original_size == 0))
276 });
277 Ok(Self {
278 archive,
279 decrypt_simple_crypt: config.xp3_simple_crypt,
280 decompress_mdf: config.xp3_mdf_decompress,
281 force_extract: config.xp3_force_extract,
282 force_decrypt: config.xp3_force_decrypt,
283 })
284 }
285}
286
287impl<'b> Script for Xp3Archive<'b> {
288 fn default_output_script_type(&self) -> OutputScriptType {
289 OutputScriptType::Json
290 }
291
292 fn default_format_type(&self) -> FormatOptions {
293 FormatOptions::None
294 }
295
296 fn is_archive(&self) -> bool {
297 true
298 }
299
300 fn iter_archive_filename<'a>(
301 &'a self,
302 ) -> Result<Box<dyn Iterator<Item = Result<String>> + 'a>> {
303 Ok(Box::new(
304 self.archive
305 .entries
306 .iter()
307 .map(|entry| Ok(entry.name.clone())),
308 ))
309 }
310
311 fn open_file<'a>(&'a self, index: usize) -> Result<Box<dyn ArchiveContent + Send + Sync + 'a>> {
312 let index = self
313 .archive
314 .entries
315 .iter()
316 .nth(index)
317 .ok_or(anyhow::anyhow!("Index out of bounds: {}", index))?
318 .clone();
319 let crypt = self.archive.crypt.clone();
320 let skip_decrypt = index.is_encrypted() && !crypt.decrypt_supported();
321 if skip_decrypt {
322 if !self.force_extract {
323 return Err(anyhow::anyhow!(
324 "The archive is encrypted with a method that is not supported by the current crypt implementation. You may need to specify a game title by using --xp3-game-title <title>."
325 ));
326 }
327 }
328 let mut entry = Entry::new(
329 self.archive.inner.clone(),
330 index,
331 self.archive.base_offset,
332 crypt,
333 skip_decrypt,
334 self.force_decrypt,
335 );
336 let mut header = [0u8; 16];
337 let header_len = entry.read(&mut header)?;
338 entry.rewind()?;
339 entry.script_type = detect_script_type(&entry.index.name, &header, header_len);
340 if self
341 .archive
342 .crypt
343 .need_filter(&entry.index.name, &header, header_len)
344 {
345 if self.archive.crypt.filter_seek_supported() {
346 let index = entry.index.clone();
347 let mut result = self.archive.crypt.filter_with_seek(entry)?;
348 let header_len = result.read(&mut header)?;
349 result.rewind()?;
350 let script_type = detect_script_type(&index.name, &header, header_len);
351 return Ok(Box::new(CustomFilterWithSeekEntry {
352 inner: result,
353 index,
354 script_type,
355 }));
356 } else {
357 let index = entry.index.clone();
358 let mut result = self.archive.crypt.filter(entry)?;
359 let header_len = result.read(&mut header)?;
360 let script_type = detect_script_type(&index.name, &header, header_len);
361 let prefix = header[..header_len].to_vec();
362 return Ok(Box::new(CustomFilterEntry {
363 inner: PrefixStream::new(prefix, result),
364 index,
365 script_type,
366 }));
367 }
368 }
369 if self.decrypt_simple_crypt
370 && header_len >= 5
371 && header[0] == 0xFE
372 && header[1] == 0xFE
373 && header[3] == 0xFF
374 && header[4] == 0xFE
375 {
376 let crypt = header[2];
377 if crypt == 2 {
378 let index = entry.index.clone();
379 return Ok(Box::new(SimpleCryptZlib::new(entry, index)?));
380 }
381 if matches!(crypt, 0 | 1) {
382 let index = entry.index.clone();
383 return Ok(Box::new(SimpleCrypt::new(entry, index, crypt)?));
384 }
385 }
386 if self.decompress_mdf
387 && header_len >= 4
388 && &header[0..4] == b"mdf\0"
389 && entry.index.original_size > 8
390 {
391 let index = entry.index.clone();
392 return Ok(Box::new(MdfEntry::new(entry, index)?));
393 }
394 Ok(Box::new(entry))
395 }
396}
397
398fn detect_script_type(filename: &str, buf: &[u8], buf_len: usize) -> Option<ScriptType> {
399 #[cfg(feature = "kirikiri-img")]
400 if buf_len >= 11 && libtlg_rs::is_valid_tlg(buf) {
401 return Some(ScriptType::KirikiriTlg);
402 }
403 if buf_len >= 8 && (buf.starts_with(b"TJS/ns0\0") || buf.starts_with(b"TJS/4s0\0")) {
404 return Some(ScriptType::KirikiriTjsNs0);
405 }
406 if buf_len >= 8 && buf.starts_with(b"TJS2100\0") {
407 return Some(ScriptType::KirikiriTjs2);
408 }
409 let extension = std::path::Path::new(filename)
410 .extension()
411 .and_then(|s| s.to_str())
412 .unwrap_or("")
413 .to_lowercase();
414 match extension.as_str() {
415 "ks" => Some(ScriptType::Kirikiri),
416 "scn" => Some(ScriptType::KirikiriScn),
417 #[cfg(feature = "emote-img")]
418 "dref" => Some(ScriptType::EmoteDref),
419 #[cfg(feature = "emote-img")]
420 "pimg" => Some(ScriptType::EmotePimg),
421 _ => None,
422 }
423}
424
425struct Entry<'a> {
426 reader: Arc<Mutex<Box<dyn ReadSeek + Send + Sync + 'a>>>,
427 index: archive::Xp3Entry,
428 crypt: Arc<Box<dyn Crypt + Send + Sync>>,
429 cache: Option<Box<dyn Read + Send + Sync + 'a>>,
431 crypt_stream: Option<Box<dyn ReadSeek + Send + Sync + 'a>>,
433 pos: u64,
434 base_offset: u64,
435 entries_pos: Vec<u64>,
436 script_type: Option<ScriptType>,
437 skip_decrypt: bool,
438 force_decrypt: bool,
439}
440
441#[automatically_derived]
442impl<'a> std::fmt::Debug for Entry<'a> {
443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444 f.debug_struct("Entry")
445 .field("reader", &self.reader)
446 .field("index", &self.index)
447 .field("crypt", &self.crypt)
448 .field("cache", &self.cache.is_some())
449 .field("crypt_stream", &self.crypt_stream)
450 .field("pos", &self.pos)
451 .field("base_offset", &self.base_offset)
452 .field("entries_pos", &self.entries_pos)
453 .field("script_type", &self.script_type)
454 .field("skip_decrypt", &self.skip_decrypt)
455 .finish()
456 }
457}
458
459impl<'a> Entry<'a> {
460 fn new(
461 reader: Arc<Mutex<Box<dyn ReadSeek + Send + Sync + 'a>>>,
462 index: archive::Xp3Entry,
463 base_offset: u64,
464 crypt: Arc<Box<dyn Crypt + Send + Sync>>,
465 skip_decrypt: bool,
466 force_decrypt: bool,
467 ) -> Self {
468 let mut pos = 0;
469 let entries_pos = index
470 .segments
471 .iter()
472 .map(|seg| {
473 let p = pos;
474 pos += seg.original_size;
475 p
476 })
477 .collect();
478 Self {
479 reader,
480 index,
481 cache: None,
482 pos: 0,
483 entries_pos,
484 script_type: None,
485 base_offset,
486 crypt,
487 crypt_stream: None,
488 skip_decrypt,
489 force_decrypt,
490 }
491 }
492
493 fn new2(
494 reader: Arc<Mutex<Box<dyn ReadSeek + Send + Sync + 'a>>>,
495 index: archive::Xp3Entry,
496 base_offset: u64,
497 crypt: Arc<Box<dyn Crypt + Send + Sync>>,
498 ) -> Self {
499 Self::new(reader, index, base_offset, crypt, false, false)
500 }
501}
502
503impl<'b> ArchiveContent for Entry<'b> {
504 fn name(&self) -> &str {
505 &self.index.name
506 }
507
508 fn size(&self) -> Option<u64> {
509 Some(self.index.archived_size)
510 }
511
512 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
513 Ok(Box::new(self))
514 }
515
516 fn script_type(&self) -> Option<&ScriptType> {
517 self.script_type.as_ref()
518 }
519}
520
521impl<'a> Read for Entry<'a> {
522 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
523 if self.pos >= self.index.original_size {
524 self.cache.take();
525 self.crypt_stream.take();
526 return Ok(0);
527 }
528 if let Some(cache) = self.cache.as_mut() {
529 let readed = cache.read(buf)?;
530 if readed > 0 {
531 self.pos += readed as u64;
532 return Ok(readed);
533 }
534 self.cache.take();
535 }
536 if let Some(crypt_stream) = self.crypt_stream.as_mut() {
537 let readed = crypt_stream.read(buf)?;
538 if readed > 0 {
539 self.pos += readed as u64;
540 return Ok(readed);
541 }
542 self.crypt_stream.take();
543 }
544 let seg_index = match self.entries_pos.binary_search(&self.pos) {
545 Ok(i) => i,
546 Err(i) => {
547 if i == 0 {
548 0
549 } else {
550 i - 1
551 }
552 }
553 };
554 let seg = &self.index.segments[seg_index];
555 let start_pos = seg.start + self.base_offset;
556 let seg_pos = self.entries_pos[seg_index];
557 let skip_pos = self.pos - seg_pos;
558 let read_size = seg.archived_size;
559 if !self.skip_decrypt
560 && (self.index.is_encrypted() || (self.force_decrypt && self.crypt.decrypt_supported()))
561 {
562 if seg.is_compressed || !self.crypt.decrypt_seek_supported() {
563 let mut cache: Box<dyn Read + Send + Sync> = if seg.is_compressed {
564 let mut inner =
565 MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
566 let decompressed = if inner.peek_and_equal(ZSTD_SIGNATURE).is_ok() {
567 Box::new(ZstdDecoder::new(inner)?) as Box<dyn Read + Send + Sync>
568 } else {
569 Box::new(ZlibDecoder::new(inner)) as Box<dyn Read + Send + Sync>
570 };
571 let decrypted =
572 self.crypt
573 .decrypt(&self.index, seg, decompressed)
574 .map_err(|e| {
575 std::io::Error::new(
576 std::io::ErrorKind::Other,
577 format!("Decryption failed: {}", e),
578 )
579 })?;
580 Box::new(decrypted) as Box<dyn Read + Send + Sync>
581 } else {
582 let inner = MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
583 let decrypted = self
584 .crypt
585 .decrypt(&self.index, seg, Box::new(inner))
586 .map_err(|e| {
587 std::io::Error::new(
588 std::io::ErrorKind::Other,
589 format!("Decryption failed: {}", e),
590 )
591 })?;
592 Box::new(decrypted) as Box<dyn Read + Send + Sync>
593 };
594 if skip_pos != 0 {
595 let mut e = EmptyWriter::new();
596 std::io::copy(&mut (&mut cache).take(skip_pos), &mut e)?; }
598 let readed = cache.read(buf)?;
599 self.pos += readed as u64;
600 self.cache = Some(cache);
601 return Ok(readed);
602 } else {
603 let inner = MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
604 let mut decrypted = self
605 .crypt
606 .decrypt_with_seek(&self.index, seg, Box::new(inner))
607 .map_err(|e| {
608 std::io::Error::new(
609 std::io::ErrorKind::Other,
610 format!("Decryption failed: {}", e),
611 )
612 })?;
613 if skip_pos != 0 {
614 let mut e = EmptyWriter::new();
615 std::io::copy(&mut (&mut decrypted).take(skip_pos), &mut e)?; }
617 let readed = decrypted.read(buf)?;
618 self.pos += readed as u64;
619 self.crypt_stream = Some(decrypted);
620 return Ok(readed);
621 }
622 }
623 if seg.is_compressed {
624 let mut inner = MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
625 let mut cache = if inner.peek_and_equal(ZSTD_SIGNATURE).is_ok() {
626 Box::new(ZstdDecoder::new(inner)?) as Box<dyn Read + Send + Sync>
627 } else {
628 Box::new(ZlibDecoder::new(inner)) as Box<dyn Read + Send + Sync>
629 };
630 if skip_pos != 0 {
631 let mut e = EmptyWriter::new();
632 std::io::copy(&mut (&mut cache).take(skip_pos), &mut e)?; }
634 let readed = cache.read(buf)?;
635 self.pos += readed as u64;
636 self.cache = Some(cache);
637 Ok(readed)
638 } else {
639 let mut lock = MutexWrapper::new(self.reader.clone(), start_pos + skip_pos);
640 let readed = (&mut lock).take(read_size - skip_pos).read(buf)?;
641 self.pos += readed as u64;
642 Ok(readed)
643 }
644 }
645}
646
647impl<'a> Seek for Entry<'a> {
648 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
649 let new_pos = match pos {
650 SeekFrom::Start(p) => p,
651 SeekFrom::End(offset) => {
652 if offset < 0 {
653 if (-offset) as u64 > self.index.original_size {
654 return Err(std::io::Error::new(
655 std::io::ErrorKind::InvalidInput,
656 "Seek from end exceeds file length",
657 ));
658 }
659 self.index.original_size - (-offset) as u64
660 } else {
661 self.index.original_size + offset as u64
662 }
663 }
664 SeekFrom::Current(offset) => {
665 if offset < 0 {
666 if (-offset) as u64 > self.pos {
667 return Err(std::io::Error::new(
668 std::io::ErrorKind::InvalidInput,
669 "Seek from current exceeds file start",
670 ));
671 }
672 self.pos - (-offset) as u64
673 } else {
674 self.pos + offset as u64
675 }
676 }
677 };
678 if let Some(cache) = self.cache.as_mut() {
679 let old_seg_index = match self.entries_pos.binary_search(&self.pos) {
680 Ok(i) => i,
681 Err(i) => {
682 if i == 0 {
683 0
684 } else {
685 i - 1
686 }
687 }
688 };
689 let new_seg_index = match self.entries_pos.binary_search(&new_pos) {
690 Ok(i) => i,
691 Err(i) => {
692 if i == 0 {
693 0
694 } else {
695 i - 1
696 }
697 }
698 };
699 if old_seg_index != new_seg_index {
700 self.cache.take();
701 } else {
702 if new_pos >= self.pos {
703 let skip_pos = new_pos - self.pos;
704 let mut e = EmptyWriter::new();
705 std::io::copy(&mut cache.take(skip_pos), &mut e)?; } else {
707 self.cache.take();
708 }
709 }
710 }
711 if let Some(crypt_stream) = self.crypt_stream.as_mut() {
712 let old_seg_index = match self.entries_pos.binary_search(&self.pos) {
713 Ok(i) => i,
714 Err(i) => {
715 if i == 0 {
716 0
717 } else {
718 i - 1
719 }
720 }
721 };
722 let new_seg_index = match self.entries_pos.binary_search(&new_pos) {
723 Ok(i) => i,
724 Err(i) => {
725 if i == 0 {
726 0
727 } else {
728 i - 1
729 }
730 }
731 };
732 if old_seg_index != new_seg_index {
733 self.crypt_stream.take();
734 } else {
735 let offset = new_pos as i64 - self.pos as i64;
736 crypt_stream.seek(SeekFrom::Current(offset))?;
737 }
738 }
739 self.pos = new_pos;
740 Ok(self.pos)
741 }
742
743 fn rewind(&mut self) -> std::io::Result<()> {
744 self.pos = 0;
745 self.cache.take();
746 self.crypt_stream.take();
747 Ok(())
748 }
749
750 fn stream_position(&mut self) -> std::io::Result<u64> {
751 Ok(self.pos)
752 }
753}
754
755struct SimpleCryptZlib<'a> {
756 inner: PrefixStream<ZlibDecoder<StreamRegion<Entry<'a>>>>,
757 index: archive::Xp3Entry,
758}
759
760impl<'a> SimpleCryptZlib<'a> {
761 fn new(mut entry: Entry<'a>, index: archive::Xp3Entry) -> Result<Self> {
762 entry.seek(SeekFrom::Start(0x15))?;
763 let entry = StreamRegion::new(entry, 0x15, index.original_size)?;
764 let inner = PrefixStream::new(vec![0xFF, 0xFE], ZlibDecoder::new(entry));
765 Ok(Self { inner, index })
766 }
767}
768
769impl<'a> ArchiveContent for SimpleCryptZlib<'a> {
770 fn name(&self) -> &str {
771 &self.index.name
772 }
773
774 fn size(&self) -> Option<u64> {
775 Some(self.index.original_size)
776 }
777}
778
779impl<'a> Read for SimpleCryptZlib<'a> {
780 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
781 self.inner.read(buf)
782 }
783}
784
785#[derive(Debug)]
786struct SimpleCryptInner<'a> {
787 inner: StreamRegion<Entry<'a>>,
788 crypt: u8,
789}
790
791impl<'a> SimpleCryptInner<'a> {
792 fn new(mut entry: Entry<'a>, crypt: u8) -> Result<Self> {
793 entry.seek(SeekFrom::Start(5))?;
794 let size = entry.index.original_size;
795 let entry = StreamRegion::new(entry, 5, size)?;
796 Ok(Self {
797 inner: entry,
798 crypt,
799 })
800 }
801}
802
803impl<'a> Read for SimpleCryptInner<'a> {
804 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
805 let readed = self.inner.read(buf)?;
806 match self.crypt {
807 0 => {
808 for b in &mut buf[..readed] {
809 let ch = *b as u16;
810 if ch >= 20 {
811 *b = wrapping! {ch ^ (((ch & 0xfe) << 8) ^ 1)} as u8;
812 }
813 }
814 }
815 1 => {
816 for b in &mut buf[..readed] {
817 let mut ch = *b as u32;
818 ch = wrapping! {((ch & 0xaaaaaaaa) >> 1) | ((ch & 0x55555555) << 1)};
819 *b = ch as u8;
820 }
821 }
822 _ => {}
823 }
824 Ok(readed)
825 }
826}
827
828impl<'a> Seek for SimpleCryptInner<'a> {
829 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
830 self.inner.seek(pos)
831 }
832
833 fn rewind(&mut self) -> std::io::Result<()> {
834 self.inner.rewind()
835 }
836
837 fn stream_position(&mut self) -> std::io::Result<u64> {
838 self.inner.stream_position()
839 }
840}
841
842#[derive(Debug)]
843struct SimpleCrypt<'a> {
844 inner: PrefixStream<SimpleCryptInner<'a>>,
845 index: archive::Xp3Entry,
846}
847
848impl<'a> SimpleCrypt<'a> {
849 fn new(entry: Entry<'a>, index: archive::Xp3Entry, crypt: u8) -> Result<Self> {
850 let inner = PrefixStream::new(vec![0xFF, 0xFE], SimpleCryptInner::new(entry, crypt)?);
851 Ok(Self { inner, index })
852 }
853}
854
855impl<'b> ArchiveContent for SimpleCrypt<'b> {
856 fn name(&self) -> &str {
857 &self.index.name
858 }
859
860 fn size(&self) -> Option<u64> {
861 Some(self.index.original_size)
862 }
863
864 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
865 Ok(Box::new(self))
866 }
867}
868
869impl<'a> Read for SimpleCrypt<'a> {
870 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
871 self.inner.read(buf)
872 }
873}
874
875impl<'a> Seek for SimpleCrypt<'a> {
876 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
877 self.inner.seek(pos)
878 }
879
880 fn rewind(&mut self) -> std::io::Result<()> {
881 self.inner.rewind()
882 }
883
884 fn stream_position(&mut self) -> std::io::Result<u64> {
885 self.inner.stream_position()
886 }
887}
888
889#[derive(Debug)]
890struct MdfEntry<'a> {
891 inner: ZlibDecoder<StreamRegion<Entry<'a>>>,
892 index: archive::Xp3Entry,
893}
894
895impl<'a> MdfEntry<'a> {
896 fn new(mut entry: Entry<'a>, index: archive::Xp3Entry) -> Result<Self> {
897 entry.seek(SeekFrom::Start(8))?;
898 let entry = StreamRegion::new(entry, 8, index.original_size)?;
899 let inner = ZlibDecoder::new(entry);
900 Ok(Self { inner, index })
901 }
902}
903impl<'a> ArchiveContent for MdfEntry<'a> {
904 fn name(&self) -> &str {
905 &self.index.name
906 }
907
908 fn size(&self) -> Option<u64> {
909 Some(self.index.original_size)
910 }
911}
912
913impl<'a> Read for MdfEntry<'a> {
914 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
915 self.inner.read(buf)
916 }
917}
918
919#[derive(Debug)]
920struct CustomFilterEntry<'a> {
921 inner: PrefixStream<Box<dyn ReadDebug + Send + Sync + 'a>>,
922 index: archive::Xp3Entry,
923 script_type: Option<ScriptType>,
924}
925
926impl<'a> Read for CustomFilterEntry<'a> {
927 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
928 self.inner.read(buf)
929 }
930}
931
932impl<'a> ArchiveContent for CustomFilterEntry<'a> {
933 fn name(&self) -> &str {
934 &self.index.name
935 }
936
937 fn size(&self) -> Option<u64> {
938 Some(self.index.original_size)
939 }
940
941 fn script_type(&self) -> Option<&ScriptType> {
942 self.script_type.as_ref()
943 }
944}
945
946#[derive(Debug)]
947struct CustomFilterWithSeekEntry<'a> {
948 inner: Box<dyn ReadSeek + Send + Sync + 'a>,
949 index: archive::Xp3Entry,
950 script_type: Option<ScriptType>,
951}
952
953impl<'a> Read for CustomFilterWithSeekEntry<'a> {
954 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
955 self.inner.read(buf)
956 }
957}
958
959impl<'a> Seek for CustomFilterWithSeekEntry<'a> {
960 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
961 self.inner.seek(pos)
962 }
963
964 fn rewind(&mut self) -> std::io::Result<()> {
965 self.inner.rewind()
966 }
967
968 fn stream_position(&mut self) -> std::io::Result<u64> {
969 self.inner.stream_position()
970 }
971}
972
973impl<'b> ArchiveContent for CustomFilterWithSeekEntry<'b> {
974 fn name(&self) -> &str {
975 &self.index.name
976 }
977
978 fn size(&self) -> Option<u64> {
979 Some(self.index.original_size)
980 }
981
982 fn script_type(&self) -> Option<&ScriptType> {
983 self.script_type.as_ref()
984 }
985
986 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
987 Ok(Box::new(self))
988 }
989}